#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <mutex>
#include <fstream>
#include <atomic>
#include <cassert>
#include <algorithm>
#include <sstream>
#include "../Common/Log.hpp"
#include "../Common/Util.hpp"
#include "../Common/httplib.h"
#include "jsoncpp/json/json.h"
#include "Model.hpp"
#include "View.hpp"
#include "Session.hpp"
namespace ns_Control
{
    using namespace FileTool;
    using namespace ns_model;
    using namespace ns_view;
    static const std::string DefaultConfPath = "./Conf/Machine.conf";
    struct Machine
    {
        std::string _ip;   // 主机ip
        int _port;         // 端口号
        int _Load_value;   // 负载值
        std::mutex *_lock; // 锁
        Machine() : _ip(""), _port(0), _Load_value(0), _lock(nullptr)
        {
        }
        // 原子性递增--提高服务器负载
        void IncLoadValue()
        {
            if (_lock)
                _lock->lock();
            ++_Load_value;
            if (_lock)
                _lock->unlock();
        }
        // 减少服务器负载
        void DecLoadValue()
        {
            if (_lock)
                _lock->lock();
            --_Load_value;
            if (_lock)
                _lock->unlock();
        }
        int GetCurrMachineLoad()
        {
            int load = 0;
            if (_lock)
                _lock->lock();
            load = _Load_value;
            if (_lock)
                _lock->unlock();
            return load;
        }
        void ReSetLoad()
        {
            if (_lock)
                _lock->lock();
            _Load_value = 0;
            if (_lock)
                _lock->unlock();
        }
    };
    class LoadBlance
    {
    private:
        std::vector<Machine> _machines;
        std::vector<int> _online;
        std::vector<int> _offline;
        std::mutex _mutex; // 大锁
    public:
        LoadBlance()
        {
            assert(LoadMachines(DefaultConfPath));
        }
        bool LoadMachines(const std::string &confpath)
        {
            std::ifstream rd(confpath.c_str());
            if (!rd.is_open())
            {
                LOG(FATAL, "配置文件加载失败！\n");
                return false;
            }
            std::string line;
            int lines = 0;
            while (std::getline(rd, line))
            {
                ++lines;
                std::vector<std::string> res;
                FileTool::StringUtil::SepString(line, &res, " ");
                if (res.size() != 2)
                {
                    LOG(WARNING, "%s文件的第%d台服务器加载错误!\n", confpath.c_str(), lines);
                    continue;
                }
                Machine m;
                m._ip = res[0];
                m._port = std::stoi(res[1]);
                m._Load_value = 0;
                m._lock = new std::mutex();
                _online.push_back(_machines.size());
                _machines.push_back(m);
            }
            LOG(INFO, "加载所有服务器信息成功!，共计%d台服务器!\n", _machines.size());
            rd.close();
            return true;
        }
        bool SmartSelectMachine(int *id, Machine **m)
        {
            // 负载均衡
            // 随机数 + hash
            // 轮询 + hash
            std::lock_guard<std::mutex> guard(_mutex);
            if (_online.size() == 0)
            {
                LOG(FATAL, "后端服务器全挂了,运维的同学快抢救一下!!!\n");
                return false;
            }
            *id = _online[0];
            *m = &_machines[_online[0]];
            int min_load = _machines[_online[0]].GetCurrMachineLoad();
            for (int i = 0; i < _online.size(); i++)
            {
                int currload = _machines[_online[i]].GetCurrMachineLoad();
                if (min_load > currload)
                {
                    min_load = currload;
                    *id = _online[i];
                    *m = &_machines[_online[i]];
                }
            }
            return true;
        }
        // 离线机器
        void OffOnlineMachine(int number)
        {
            // 保证安全
            std::lock_guard<std::mutex> lock(_mutex);
            // 遍历找到在线机器
            for (auto iter = _online.begin(); iter != _online.end(); iter++)
            {
                if (*iter == number)
                {
                    _machines[number].ReSetLoad();
                    _online.erase(iter);
                    _offline.push_back(number);
                    break;
                }
            }
        }
        // 上线机器
        void OnlineMachine()
        {
            std::lock_guard<std::mutex> lock(_mutex);
            _online.insert(_online.end(), _offline.begin(), _offline.end());
            _offline.erase(_offline.begin(), _offline.end());
        }
        ~LoadBlance()
        {
        }
    };
    class Control
    {
    private:
        Model _model;
        View _view;
        LoadBlance _blance;
        Session _session;
    public:
        Control() {}
        ~Control() {}
        void RecOnlineMachine()
        {
            _blance.OnlineMachine();
        }
        // 登录逻辑
        bool Login(const std::string &data, std::string *out,uint64_t sessionid = -1)
        {
            std::cout<<data<<std::endl;
            printf("进入了login执行了!\n");
            // 解析账号
            auto pos1 = data.find(R"(:")") + 2;
            auto pos2 = data.find(R"(",)");
            size_t x = pos2 - pos1;
            std::string user = data.substr(pos1, x);
            printf("获取了用户: %s\n",user.c_str());
            // 解析密码
            auto pos3 = data.rfind(R"(:)") + 2;
            auto pos4 = data.rfind(R"(")");
            size_t y = pos4 - pos3;
            std::string password = data.substr(pos3, y);
             printf("密码: %s\n",password.c_str());
            //进入session判断，如果存在则允许跳转并且返回session--id，否则新增
            uint64_t id = _session.Detect(user,password);
            (*out) += std::to_string(id);
            return true;
        }
        bool AllQuestions(std::string *html)
        {
            std::vector<Question> res;
            if (_model.GetAllQuestionList(&res))
            {
                // 构建网页
                _view.SetALLQuestionHtml(res, html);
                // 排序
                std::sort(res.begin(), res.end(), [](const Question &px, const Question &py)
                          { return std::stoi(px._id) < std::stoi(py._id); });
                return true;
            }
            else
            {
                *html = "构建全部题目页面失败！";
                return false;
            }
            return false;
        }
        bool OnceQuestion(const std::string &id, std::string *html)
        {
            Question q;
            if (_model.GetOnceQuestion(id, &q))
            {
                _view.SetOnceQuestionHtml(q, html);
                return true;
            }
            else
            {
                *html = "构建" + id + "号题目页面失败！";
                return false;
            }
        }

        void Judge(const std::string &number, const std::string &injson, std::string *outjson)
        {
            // 0.获取指定的题目的用户提交信息
            Question q;
            _model.GetOnceQuestion(number, &q);
            // 1.反序列化
            Json::CharReaderBuilder build_rd;
            build_rd["emitUTF8"] = true;
            std::unique_ptr<Json::CharReader> rd(build_rd.newCharReader());
            Json::Value in_value;
            std::string error_parse;
            rd->parse(injson.c_str(), injson.c_str() + injson.size(), &in_value, &error_parse);

            // 2.返回给编译运行模块
            Json::Value compiler_value;
            compiler_value["input"] = in_value["input"].asString();
            std::string code = in_value["code"].asString();
            compiler_value["code"] = code + q._tail; ///!!最重要的一步，合并用户提交的代码和测试用例
            compiler_value["cpu_limit"] = q._cpu_limit;
            compiler_value["mem_limit"] = q._mem_limit;
            // 3.序列化返回给下层的编译运行服务
            Json::StreamWriterBuilder build_wr;
            build_wr["emitUTF8"] = true;
            std::unique_ptr<Json::StreamWriter> wd(build_wr.newStreamWriter());
            std::stringstream ss;
            wd->write(compiler_value, &ss);
            // 4.负载均衡的选择发送的服务器
            while (true)
            {
                // 轮询
                int id = 0;
                Machine *m = nullptr;
                if (!_blance.SmartSelectMachine(&id, &m))
                {
                    break;
                }
                // 发送http请求
                httplib::Client client(m->_ip, m->_port);
                m->IncLoadValue();
                if (auto res = client.Post("/Compiler_and_Run", ss.str(), "application/json;charset=utf-8"))
                {
                    // 考虑状态码
                    if (res->status == 200)
                    {
                        m->DecLoadValue();
                        *outjson = res->body;
                        LOG(INFO, "编译运行请求成功!\n");
                        break;
                    }
                    m->DecLoadValue();
                }
                else
                {
                    LOG(ERROR, "本次请求的机器可能已经下线! address: %s : %d\n", m->_ip.c_str(), m->_port);
                    // 离线该机器
                    _blance.OffOnlineMachine(id);
                }
            }
        }
    };
}